package org.vaadin.golfscoretracker.ui.views;

import org.vaadin.appfoundation.i18n.Lang;
import org.vaadin.appfoundation.persistence.data.AbstractPojo;
import org.vaadin.appfoundation.view.AbstractView;
import org.vaadin.golfscoretracker.ui.controller.DataEditorController;

import com.vaadin.data.Container;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.data.util.BeanItem;
import com.vaadin.ui.Button;
import com.vaadin.ui.Form;
import com.vaadin.ui.FormFieldFactory;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Layout;
import com.vaadin.ui.SplitPanel;
import com.vaadin.ui.Table;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;

public abstract class AbstractDataEditorView<A extends AbstractPojo> extends
        AbstractView<SplitPanel> implements ClickListener, ValueChangeListener {

    private static final long serialVersionUID = 8128220262177870364L;
    private VerticalLayout topLayout;
    protected Table table;
    private Button addBtn;
    private Button removeBtn;

    private VerticalLayout editorLayout;
    private Button saveBtn;
    private Button cancelBtn;

    private Form form;

    private Container container;

    private final DataEditorController<A> controller;

    public AbstractDataEditorView(DataEditorController<A> controller) {
        super(new SplitPanel());
        this.controller = controller;
        getContent().setSizeFull();

        initTopLayout();
        addedTableToView();
        initEditLayout();
        addedEditorToView();
    }

    private void initTopLayout() {
        topLayout = new VerticalLayout();
        Table table = initPlayersTable();
        topLayout.addComponent(table);
        Layout layout = initTopButtons();
        topLayout.addComponent(layout);
        topLayout.setExpandRatio(table, 1);
        topLayout.setSizeFull();
    }

    private Layout initTopButtons() {
        HorizontalLayout layout = new HorizontalLayout();
        addBtn = new Button(Lang.getMessage("add"), this);
        removeBtn = new Button(Lang.getMessage("remove"), this);

        layout.addComponent(addBtn);
        layout.addComponent(removeBtn);
        return layout;
    }

    private Table initPlayersTable() {
        table = new Table();
        table.setSizeFull();
        container = controller.getContainer();
        table.setContainerDataSource(container);
        table.setSelectable(true);
        table.addListener(this);
        table.setImmediate(true);
        setTableHeaders();
        return table;
    }

    protected abstract void setTableHeaders();

    private void initForm() {
        form = new Form();
        form.setFormFieldFactory(getFormFieldFactory());
    }

    protected abstract FormFieldFactory getFormFieldFactory();

    private void initEditLayout() {
        editorLayout = new VerticalLayout();
        initForm();
        editorLayout.addComponent(form);
        Layout layout = initEditorButtons();
        editorLayout.addComponent(layout);
        editorLayout.setExpandRatio(form, 1);
        editorLayout.setSizeFull();
    }

    private Layout initEditorButtons() {
        HorizontalLayout layout = new HorizontalLayout();
        saveBtn = new Button(Lang.getMessage("save"), this);
        cancelBtn = new Button(Lang.getMessage("cancel"), this);
        saveBtn.setEnabled(false);
        cancelBtn.setEnabled(false);

        layout.addComponent(saveBtn);
        layout.addComponent(cancelBtn);
        return layout;
    }

    private void addedTableToView() {
        getContent().setFirstComponent(topLayout);
    }

    private void addedEditorToView() {
        getContent().setSecondComponent(editorLayout);
    }

    @Override
    public void activated(Object... params) {
        // TODO Auto-generated method stub

    }

    @Override
    public void deactivated(Object... params) {
        // TODO Auto-generated method stub

    }

    @SuppressWarnings("unchecked")
    @Override
    public void buttonClick(ClickEvent event) {
        if (event.getButton().equals(addBtn)) {
            A pojo = controller.createPojo();
            controller.addItem(container, pojo);
            table.select(pojo);
        } else if (event.getButton().equals(saveBtn)) {
            if (form.getItemDataSource() == null) {
                return;
            }
            try {
                form.validate();
                form.commit();
                BeanItem<A> item = (BeanItem<A>) form.getItemDataSource();
                A pojo = item.getBean();
                controller.save(pojo);
                controller.updateItemProperties(container, pojo);
                table.select(null);
                form.setValidationVisible(false);
            } catch (InvalidValueException e) {
                form.setValidationVisible(true);
            }
        } else if (event.getButton().equals(cancelBtn)) {
            if (form.getItemDataSource() == null) {
                return;
            }
            A pojo = (A) table.getValue();
            form.discard();
            table.select(null);
            if (pojo.getId() == null) {
                container.removeItem(pojo);
            }
            form.setValidationVisible(false);
        } else if (event.getButton().equals(removeBtn)) {
            Object value = table.getValue();
            if (value == null) {
                return;
            }
            table.select(null);
            container.removeItem(value);
            controller.remove((A) value);
            form.setValidationVisible(false);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void valueChange(ValueChangeEvent event) {
        Object value = table.getValue();
        if (value == null) {
            form.setItemDataSource(null);
            saveBtn.setEnabled(false);
            cancelBtn.setEnabled(false);
            return;
        }

        try {
            A pojo = (A) value;
            saveBtn.setEnabled(true);
            cancelBtn.setEnabled(true);
            setPojoToForm(pojo);
        } catch (ClassCastException ignore) {
            // ignore
        }
    }

    private void setPojoToForm(A pojo) {
        form.setValidationVisible(false);
        BeanItem<A> item = new BeanItem<A>(pojo);
        form.setItemDataSource(item);
        form.setVisibleItemProperties(getVisibleItemProperties());
        form.focus();
    }

    protected abstract Object[] getVisibleItemProperties();
}